#include "StdAfx.h"

#include "qfile.h"
using namespace micro::files;


//#include <Wdm.h>
//#pragma comment(lib, "Ntoskrnl.lib")


//for FSCTL_SET_REPARSE_POINT
//DeviceIoControl
#include <winioctl.h>
#include <intsafe.h>  //LODWORD   HIDWORD
//DeleteVolumeMountPoint


namespace  micro
{
namespace files
{

typedef struct _REPARSE_DATA_BUFFER
{
    ULONG  ReparseTag;
    USHORT ReparseDataLength;
    USHORT Reserved;
    union
    {
        struct
        {
            USHORT SubstituteNameOffset;
            USHORT SubstituteNameLength;
            USHORT PrintNameOffset;
            USHORT PrintNameLength;
            ULONG Flags;
            WCHAR PathBuffer[1];
        } SymbolicLinkReparseBuffer;
        struct
        {
            USHORT SubstituteNameOffset;
            USHORT SubstituteNameLength;
            USHORT PrintNameOffset;
            USHORT PrintNameLength;
            WCHAR PathBuffer[1];
        } MountPointReparseBuffer;
        struct
        {
            UCHAR  DataBuffer[1];
        } GenericReparseBuffer;
    } DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;


///for reparse
#define REPARSE_DATA_BUFFER_HEADER_SIZE   FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)

//for sparse
NTSTATUS NTAPI NtQueryInformationFile(
    IN HANDLE FileHandle,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    OUT PVOID FileInformation,
    IN ULONG Length,
    IN FILE_INFORMATION_CLASS FileInformationClass )
{
    // Define pointer to function type for dynamic loading
    typedef NTSTATUS ( NTAPI * NTQUERYINFORMATIONFILE )(
        IN HANDLE FileHandle,
        OUT PIO_STATUS_BLOCK IoStatusBlock,
        OUT PVOID FileInformation,
        IN ULONG Length,
        IN FILE_INFORMATION_CLASS FileInformationClass );


    static NTQUERYINFORMATIONFILE pNtQueryInformationFile = nullptr;

    if ( nullptr == pNtQueryInformationFile )
    {
        HINSTANCE ntdll = ::GetModuleHandle( L"ntdll.dll" );
        pNtQueryInformationFile = ( NTQUERYINFORMATIONFILE ) ::GetProcAddress( ntdll, "NtQueryInformationFile" );
    }

    return	pNtQueryInformationFile( FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass );
}

}
}



QFile::QFile()
{
    m_hFile = 0;
}

QFile::~QFile()
{
    Close();
}

QFile::QFile( const QString & lpszFileName, UINT nOpenFlags )
{

    Open( lpszFileName, nOpenFlags );

}


bool QFile::Open( LPCTSTR lpszFileName, UINT nOpenFlags, QError * pError )
{
    m_strFileName	= lpszFileName;
    m_OpenMode		= ( OpenFlags )nOpenFlags;



    DWORD dwAccess = 0;

    switch ( nOpenFlags & 3 )
    {
        case modeRead:
            dwAccess = GENERIC_READ;
            break;

        case modeWrite:
            dwAccess = GENERIC_WRITE;
            break;

        case modeReadWrite:
            dwAccess = GENERIC_READ | GENERIC_WRITE;
            break;

        default:
            ASSERT( FALSE ); // invalid share mode
    }



    DWORD dwShareMode = 0;

    switch ( nOpenFlags & 0x70 )  // map compatibility mode to exclusive
    {

        case shareCompat:
            {
                if ( dwAccess & GENERIC_READ )
                    dwShareMode = FILE_SHARE_READ;
                else if ( dwAccess & GENERIC_WRITE )
                {
                    dwShareMode = FILE_SHARE_READ;
                }

                break;
            }

        case shareExclusive:
            dwShareMode = 0;
            break;

        case shareDenyWrite:
            dwShareMode = FILE_SHARE_READ;
            break;

        case shareDenyRead:
            dwShareMode = 0;//FILE_SHARE_WRITE;
            break;

        case sharePermitAll:
            dwShareMode = FILE_SHARE_WRITE | FILE_SHARE_READ;
            break;

        default:
            ASSERT( false ); // invalid share mode?
    }



    DWORD dwCreateFlag = OPEN_EXISTING;

    if ( nOpenFlags & modeCreate )
    {
        if ( nOpenFlags & modeNoTruncate )
            dwCreateFlag = OPEN_ALWAYS;
        else
        {
            dwCreateFlag = CREATE_ALWAYS;
            ///daca e create forteaza scriere
            dwAccess |= GENERIC_WRITE;
        }
    }




    DWORD dwFlags = FILE_ATTRIBUTE_NORMAL;

    if ( nOpenFlags & osNoBuffer )
        dwFlags |= FILE_FLAG_NO_BUFFERING;

    if ( nOpenFlags & osWriteThrough )
        dwFlags |= FILE_FLAG_WRITE_THROUGH;

    if ( nOpenFlags & osRandomAccess )
        dwFlags |= FILE_FLAG_RANDOM_ACCESS;

    if ( nOpenFlags & osSequentialScan )
        dwFlags |= FILE_FLAG_SEQUENTIAL_SCAN;


    m_hFile = ::CreateFile( m_strFileName, dwAccess , dwShareMode, 0, dwCreateFlag, dwFlags, 0 );

    if ( INVALID_HANDLE_VALUE == m_hFile )
        m_hFile = nullptr;


    return m_hFile != nullptr;
}

bool QFile::IsClosed() const
{
    return m_hFile == nullptr;
}

ULONGLONG QFile::Seek( LONGLONG offset, DWORD method )
{
    LARGE_INTEGER value, newvalue;
    value.QuadPart = offset;
    newvalue.QuadPart = 0;

    BOOL bo =  ::SetFilePointerEx( m_hFile, value, &newvalue, method );

    if ( !bo )
        throw QError();

    return newvalue.QuadPart;
}

ULONGLONG QFile::SeekToEnd()
{
    return Seek( 0,  end );
}

ULONGLONG QFile::SeekToBegin()
{
    return Seek( 0,  begin );
}


/// not tested
bool QFile::LockRange( ULONGLONG dwPos, ULONGLONG dwCount )
{
    // Lock the .tar file
    OVERLAPPED Overlapped = { 0 }; //aici se pune pos
    Overlapped.Offset = LODWORD( dwPos );
    Overlapped.OffsetHigh = HIDWORD( dwPos );

    return TRUE == ::LockFileEx( m_hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, LODWORD( dwCount ) , HIDWORD( dwCount ), &Overlapped );
}

/// not tested
bool QFile::UnlockRange( ULONGLONG dwPos, ULONGLONG dwCount )
{
    return TRUE == ::UnlockFile( m_hFile, LODWORD( dwPos ), HIDWORD( dwPos ), LODWORD( dwCount ) , HIDWORD( dwCount ) );
}

ULONGLONG QFile::GetPosition( void ) const
{
    LARGE_INTEGER value;
    value.QuadPart = 0;

    //return Seek(0, current);

    ::SetFilePointerEx( m_hFile, value , &value, FILE_CURRENT );
    return value.QuadPart;
}

void QFile::SetFileSize( ULONGLONG dwNewLen )
{
    Seek( dwNewLen, begin );
    ::SetEndOfFile( m_hFile );
}

LONGLONG QFile::GetFileSize() const
{
    LARGE_INTEGER fileSize;
    GetFileSizeEx( m_hFile,  &fileSize );
    return fileSize.QuadPart;
}


QString QFile::GetFileName( void )const
{
    return  m_strFileName;
}
//QString QFile::GetFileTitle(void)const
//{
//	return  m_strFileName;
//}

QString QFile::GetFilePath( void )const
{
    return  m_strFileName;
}


void QFile::SetFilePath( LPCTSTR lpszNewName )
{
    ASSERT( this );

    if ( lpszNewName != nullptr )
        m_strFileName = lpszNewName;
    else
        throw QError();

}
size_t QFile::Read( void * lpBuf, size_t nCount )
{
    DWORD   readed = 0;
    Read( lpBuf, ( DWORD )nCount, readed );
    return readed;
}
size_t QFile::Write( const void * lpBuf, size_t nCount )
{
    DWORD   writed = 0;
    Write( lpBuf, ( DWORD )nCount, writed );
    return writed;
}


bool QFile::Read( void * data, DWORD nCount, DWORD & readed )
{
    readed = 0;
    return ::ReadFile( m_hFile, data, nCount, &readed, nullptr ) != 0  && readed;
}

bool QFile::Write( const void * data, DWORD nCount, DWORD & writed )
{
    writed = 0;
    return 0 != ::WriteFile( m_hFile, data, nCount, &writed, nullptr );
}

void QFile::WriteLine( const QString & str )
{

    QString s = _T( "\n" );

    DWORD written = 0;
    Write( ( const void * )( LPCTSTR )str, str.GetByteSize(), written );
    Write( ( const void * )( LPCTSTR )s, s.GetByteSize(), written );


}


bool QFile::WriteString( const QString & str )
{


    DWORD written = 0;
    int sz = str.GetByteSize() + 2;

    bool bo = Write( ( const void * ) &sz, sizeof( sz ), written );

    if ( bo )
        bo = Write( ( const void * )( LPCTSTR )str, sz, written );

    return bo;
}

bool QFile::ReadString( QString & s )
{
    DWORD readed = 0;
    int sz = 0 ;

    if ( !Read( ( void * ) &sz, sizeof( sz ), readed ) )
        return false;

    TCHAR * str = new TCHAR[sz];
    bool bo = Read( ( void * ) str, sz, readed );

    if ( readed == sz )
    {
        //str[sz]=L'0';
        s = str;
    }

    delete[] str;

    return bo;

}


// Flush the file buffers.
void QFile::Flush()
{
    ASSERT( m_hFile != INVALID_HANDLE_VALUE ); // Must be valid.

    // Flush the buffers.
    ::FlushFileBuffers( m_hFile );

}

void QFile::Abort()
{
    // Is the handle valid?
    if ( m_hFile != INVALID_HANDLE_VALUE )
    {
        // Close the handle.
        ::CloseHandle( m_hFile );

        // Reset handle value.
        m_hFile = INVALID_HANDLE_VALUE;
    }
}

// Close the existing handle.
void QFile::Close()
{
    // Close the handle.
    if ( m_hFile != INVALID_HANDLE_VALUE )
    {
        ::CloseHandle( m_hFile );
    }

    // Reset handle value.
    m_hFile = INVALID_HANDLE_VALUE;
}


///////////////////////////////////////////////////////////////////////////////////////////////
//reparse


//need  folder presence
QReparsePoint::QReparsePoint( const QString & str, bool create )
{
    const DWORD dwFA = GetFileAttributes( str );

    if ( dwFA == 0xffffffff || !( dwFA & FILE_ATTRIBUTE_REPARSE_POINT ) )
    {
        SetLastError( ERROR_PATH_NOT_FOUND );
        m_hDir = INVALID_HANDLE_VALUE;
    }

    m_hDir = CreateFile( str,
                         ( create ? GENERIC_WRITE : 0 ), //FILE_READ_EA  FILE_WRITE_EA
                         0, //FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
                         0,
                         OPEN_EXISTING,
                         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
                         0 );


    if ( m_hDir == INVALID_HANDLE_VALUE )
    {
        throw QError();
        //e.PrintMessage();
    }

}

QReparsePoint::~QReparsePoint()
{
    if ( m_hDir != INVALID_HANDLE_VALUE )
    {
        ::CloseHandle( m_hDir );
    }
}

//
bool QReparsePoint::SetReparsePointJunction( QString target )
{
    if ( !IsValid() )
        return false;


    target = QPREFFIX + target;


    BYTE szBuff[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
    REPARSE_DATA_BUFFER & buff = ( REPARSE_DATA_BUFFER & ) szBuff;

    memset( szBuff, 0 , MAXIMUM_REPARSE_DATA_BUFFER_SIZE );


    buff.ReparseTag           = IO_REPARSE_TAG_MOUNT_POINT;//jonctiune
    buff.ReparseDataLength    = target.GetByteSize() + 12;
    buff.Reserved             = 0;
    buff.MountPointReparseBuffer.SubstituteNameOffset = 0;
    buff.MountPointReparseBuffer.SubstituteNameLength = target.GetByteSize();//ok
    buff.MountPointReparseBuffer.PrintNameOffset      = target.GetByteSize() + 2 ; //ok
    buff.MountPointReparseBuffer.PrintNameLength      = 0;//OK


    _tcscpy_s( buff.MountPointReparseBuffer.PathBuffer,  MAX_PATH ,  target );

    //_tcscpy_s(buff.MountPointReparseBuffer.PathBuffer + target.GetLength() + 1  ,  target.GetByteSize()+2 ,  4+(TCHAR*)target);


    BOOL bOK = TRUE;

    DWORD dwBytes = 0;
    bOK  =	DeviceIoControl( m_hDir,
                             FSCTL_SET_REPARSE_POINT,
                             ( LPVOID )szBuff,
                             buff.ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE,
                             nullptr,
                             0,
                             &dwBytes,
                             0 );

    if ( !bOK )
        throw QError();


    return  bOK != 0 ;
}

int  QReparsePoint::GetNumberOfLinks()
{
    if ( ! IsValid() )
        return 0;

    int NumberOfLinks = 1;

    if ( m_hDir != INVALID_HANDLE_VALUE )
    {
        BY_HANDLE_FILE_INFORMATION bhfi;

        if ( GetFileInformationByHandle( m_hDir, &bhfi ) )
        {
            NumberOfLinks = bhfi.nNumberOfLinks;
        }
    }

    return NumberOfLinks;
}




///for files only
///same partition
bool QReparsePoint::CreateDirectHardLink ( QString source,  QString target )
{

    if ( ! CreateHardLink ( source, target, nullptr ) )
        throw QError();

    return  true;
}


///ADMIN ONLY
///for files or folders - no prefix
///it will create nessesary folder or file
bool QReparsePoint::CreateDirectSymLink( QString source,  QString target )
{
    DWORD flag = 0;  //target is file

    const DWORD dwFA = GetFileAttributes( target );

    if ( dwFA == INVALID_FILE_ATTRIBUTES )
        throw QError();



    if ( dwFA & FILE_ATTRIBUTE_DIRECTORY )
        flag = SYMBOLIC_LINK_FLAG_DIRECTORY;


    if ( ! CreateSymbolicLink( source, target, flag ) )
        throw QError();


    return  true;
}

///ADMIN ONLY

bool QReparsePoint::CreateMountPoint( QString folder,  QString drive )
{

    const int VOL_NAME_MAX = 80;

    BOOL fResult;

    TCHAR szUniqueVolumeName[VOL_NAME_MAX];
    TCHAR szDriveName[4];

    szDriveName[0] = drive[0];
    szDriveName[1] = drive[1];
    szDriveName[2] = L'\\';
    szDriveName[3] = L'\0';

    // now the directory name
    folder.AddBackslash();


    // Create the mount point...
    fResult = GetVolumeNameForVolumeMountPoint ( szDriveName,
              szUniqueVolumeName,
              VOL_NAME_MAX );

    if ( !fResult )
        throw QError();


    fResult = SetVolumeMountPoint ( folder, szUniqueVolumeName );

    if ( !fResult )
        throw QError();


    return fResult != 0;
}





//ADMIN ONLY  substituted by CreateDirectSymLink
////bool QReparsePoint::SetReparsePointSymlink(QString target)
////{
////	if (!IsValid()) 	return false;
////
////
////	target = QPREFFIX + target;
////
////	BYTE szBuff[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
////	REPARSE_DATA_BUFFER &buff = (REPARSE_DATA_BUFFER &) szBuff;
////	memset(szBuff, 0 , MAXIMUM_REPARSE_DATA_BUFFER_SIZE );
////
////	buff.ReparseTag         =  IO_REPARSE_TAG_SYMLINK;//symlink
////
////	if ( 1 /* IsAbsolutePath(Target) */ )
////	{
////		buff.SymbolicLinkReparseBuffer.Flags  = 0;
////	}
////	else
////	{
////		buff.SymbolicLinkReparseBuffer.Flags  = 1;//SYMLINK_FLAG_RELATIVE;
////	}
////
////
////	buff.Reserved             = 0;
////
////
////	buff.SymbolicLinkReparseBuffer.SubstituteNameOffset  = 0;
////	buff.SymbolicLinkReparseBuffer.SubstituteNameLength = target.GetByteSize();
////
////	buff.SymbolicLinkReparseBuffer.PrintNameOffset = 0;
////	buff.SymbolicLinkReparseBuffer.PrintNameLength = target.GetByteSize();
////	buff.ReparseDataLength = buff.SymbolicLinkReparseBuffer.SubstituteNameLength + 12;
////
////
////
////	_tcscpy_s(buff.SymbolicLinkReparseBuffer.PathBuffer,  target.GetByteSize() ,  target);
////
////
//// 	BOOL bOK = TRUE;
////
////		DWORD dwBytes = 0;
////		bOK  =	DeviceIoControl(m_hDir,
////							FSCTL_SET_REPARSE_POINT,
////							(LPVOID)szBuff,
////							buff.ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE,
////							nullptr,
////							0,
////							&dwBytes,
////							0);
////
////		if(!bOK)
////			throw QError();
////
////
////
////	return bOK != 0;
////}

bool QReparsePoint::DeleteReparsePoint()
{
    if ( !IsValid() )
        return false;

    DWORD ReparseTag = 0;
    QString str;

    GetReparsePoint( str,  &ReparseTag );
    REPARSE_GUID_DATA_BUFFER rgdb = { ReparseTag };

    DWORD dwBytes = 0;
    const BOOL bOK =	DeviceIoControl( m_hDir,
                                         FSCTL_DELETE_REPARSE_POINT,
                                         &rgdb,
                                         REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
                                         nullptr,
                                         0,
                                         &dwBytes,
                                         0 );
    return bOK != 0;
}




// It is known that in network symbolic links are resolved on clients, while junctions are resolved by server.
// One can suggest, that junctions are more "invisible" for users.

bool QReparsePoint::GetReparsePoint( QString & target, DWORD * reparseTag )
{
    if ( !IsValid() )
        return false;



    QString localTarget ;

//asta merge pt SYMLINK dar nu si pentru toate JUNCTION
//     TCHAR Path[MAX_PATH];
//     DWORD dwRet = GetFinalPathNameByHandle( m_hDir, Path, MAX_PATH, VOLUME_NAME_DOS );
//
//
// 		localTarget = Path;
//
//     if ( localTarget.Left( 4 ).Compare( QPREFFIX2 )  )
//         target = localTarget;
//     else
//     {
//         target = localTarget.Mid( 4 );
//     }
//     return dwRet;



    BYTE szBuff[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = { 0 };
    //memset(szBuff, 0 , MAXIMUM_REPARSE_DATA_BUFFER_SIZE );

    REPARSE_DATA_BUFFER & buff = ( REPARSE_DATA_BUFFER & ) szBuff;

    DWORD dwBytesReturned = 0;

    if ( !DeviceIoControl( m_hDir,
                           FSCTL_GET_REPARSE_POINT,
                           nullptr,
                           0,
                           ( LPVOID )szBuff,
                           MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
                           &dwBytesReturned,
                           0 )

       )
    {
        QError e;
        e.PrintMessage();
        return false;
    }


    if ( reparseTag )
        *reparseTag =	buff.ReparseTag;





    if ( IsReparseTagMicrosoft( buff.ReparseTag ) )
    {
        if ( buff.ReparseTag == IO_REPARSE_TAG_SYMLINK )
        {
            size_t slen = buff.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof( WCHAR );
            WCHAR * szSubName = new WCHAR[slen + 1];
            wcsncpy_s( szSubName, slen + 1, &buff.SymbolicLinkReparseBuffer.PathBuffer[buff.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof( WCHAR )], slen );
            szSubName[slen] = 0;

            localTarget = szSubName;

            //printf( "SubstitutionName (len: %d): '%S'\n", buff.SymbolicLinkReparseBuffer.SubstituteNameLength, szSubName );
            delete [] szSubName;

//             size_t plen = buff.SymbolicLinkReparseBuffer.PrintNameLength / sizeof( WCHAR );
//              WCHAR* szPrintName = new WCHAR[plen + 1];
//             wcsncpy_s( szPrintName, plen + 1, &buff.SymbolicLinkReparseBuffer.PathBuffer[buff.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof( WCHAR )], plen );
//             szPrintName[plen] = 0;


            //printf( "PrintName (len: %d): '%S'\n", buff.SymbolicLinkReparseBuffer.PrintNameLength, szPrintName );
//            delete [] szPrintName;
        }
        else if ( buff.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT )
        {
            //printf( "Mount-Point\n" );
            size_t slen = buff.MountPointReparseBuffer.SubstituteNameLength / sizeof( WCHAR );
            WCHAR * szSubName = new WCHAR[slen + 1];
            wcsncpy_s( szSubName, slen + 1, &buff.MountPointReparseBuffer.PathBuffer[buff.MountPointReparseBuffer.SubstituteNameOffset / sizeof( WCHAR )], slen );
            szSubName[slen] = 0;

            localTarget = szSubName;

            //printf( "SubstitutionName (len: %d): '%S'\n", rdata->MountPointReparseBuffer.SubstituteNameLength, szSubName );
            delete [] szSubName;


//                 size_t plen = buff.MountPointReparseBuffer.PrintNameLength / sizeof( WCHAR );
//                 WCHAR* szPrintName = new WCHAR[plen + 1];
//                 wcsncpy_s( szPrintName, plen + 1, &buff.MountPointReparseBuffer.PathBuffer[buff.MountPointReparseBuffer.PrintNameOffset / sizeof( WCHAR )], plen );
//                 szPrintName[plen] = 0;

//					printf( "PrintName (len: %d): '%S'\n", rdata->MountPointReparseBuffer.PrintNameLength, szPrintName );
//					delete [] szPrintName;

        }
        else
        {
            //  can't  detect target, No Mount-Point or Symblic-Link
            return false;
        }

    }
    else
    {
        //not microsoft, can't query
        return false;
    }



    if ( localTarget.Left( 4 ).Compare( QPREFFIX )  )
        target = localTarget;
    else
    {
        target = localTarget.Mid( 4 );
    }

    return true;
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////
//FindFile


bool QFindFile::IsDots() const
{


    if ( !_tcscmp( m_findData.cFileName, QDOTSTR ) )
        return true;

    if ( !_tcscmp( m_findData.cFileName, QDOTDOT ) )
        return true;


    return false;

}


// Constructor. Initializes data.
QFindFile::QFindFile()
{
    // Initialize data.
    m_bFoundFile = false;
    m_hHandle   = nullptr;
    m_dwDontShow	 = 0
                       //FILE_ATTRIBUTE_HIDDEN
                       //| FILE_ATTRIBUTE_SYSTEM
                       //| FILE_ATTRIBUTE_TEMPORARY
                       | FILE_ATTRIBUTE_OFFLINE;

    memset( ( void * )&m_findData, 0, sizeof( WIN32_FIND_DATA ) );
}

// Destructor. Closes the handle when it
// is still opened.
QFindFile::~QFindFile()
{
    // Close the handle is it is still
    // open.
    // Close the handle.
    if ( m_hHandle )
        ::FindClose( m_hHandle );
}

// Get the length
ULONGLONG QFindFile::GetFileSize() const
{
    //ASSERT( m_bFoundFile );
    return ( ( ( ULONGLONG ) m_findData.nFileSizeHigh ) << 32 ) + m_findData.nFileSizeLow;
}

QString QFindFile::GetFileName() const
{
    QString strName;

    ASSERT( m_bFoundFile );

    // Find the last slash.
    LPCTSTR pszSlash = _tcsrchr(  m_findData.cFileName , QDIRDELIMITER );

    if ( pszSlash )
    {
        // Get a pointer to the next character which is
        // either a 0-character or the first character
        // of the file name.
        pszSlash = _tcsinc( pszSlash );

        // Set the string to the result.
        strName = pszSlash;
    }
    else
        // Use the whole name.
        strName =  m_findData.cFileName ;

    return strName;
}


QString QFindFile::GetFilePath() const
{
    ASSERT( m_bFoundFile );

    QString str = m_strBase;

    str  +=  m_findData.cFileName;
    return str;
}

//// Get the file name excluding extention.
//QString QFindFile::GetFileTitle() const
//{
//	ASSERT( m_bFoundFile );
//
//	// First get the file name.
//	QString strName = GetFileName( );
//
//	// Find the offset of the extention.
//	int nPos = strName.ReverseFind( QDOT );
//	if ( nPos != -1 )
//		// Truncate the name here.
//		strName.SetStringLength( nPos );
//
//	return strName;
//}
//

// Get root directory of the file.
QString  QFindFile::GetFolder() const
{
    ASSERT( m_bFoundFile );
    return  m_strBase;

}



// Close the handle.
void QFindFile::Close()
{
    // Close the handle.
    if ( m_hHandle )
    {
        ::FindClose( m_hHandle );
        m_hHandle = nullptr;
    }

    // Reset data.
    m_strBase.Empty();
    m_bFoundFile = false;
    //memset(( void * )&m_findData, 0, sizeof( WIN32_FIND_DATA ));
}

// Open the handle.
bool QFindFile::FindFile( const  QString & strName   )
{
    ASSERT( m_hHandle == nullptr || m_hHandle == INVALID_HANDLE_VALUE ); // Should not be is use!

    // Save the base.
    m_strBase = strName;

    // Make sure we only have the directory.
    int nPos = m_strBase.ReverseFind( QDIRDELIMITER );

    if ( nPos != -1 )
        m_strBase.SetStringLength( nPos + 1 );

    // Open the handle.
    if ( ( m_hHandle = ::FindFirstFile( strName, &m_findData ) ) != INVALID_HANDLE_VALUE )
        m_bFoundFile = true;
    else
        m_bFoundFile = false;

    return m_bFoundFile;
}

// Get the next name.
bool QFindFile::FindNextFile()
{
    ASSERT( m_bFoundFile );

    // Get the next file name.
    m_bFoundFile = ( bool ) ( 0 != ::FindNextFile( m_hHandle, &m_findData ) ) ;
    return m_bFoundFile;
}






NTSTATUS GetStreamInfo( HANDLE hFile )
{
    IO_STATUS_BLOCK         IoStatus;
    DWORD                   dwBufferSize = STREAMBUFFERSIZE;
    FILE_STREAM_INFORMATION * pFsi = nullptr, *m_pFsi = nullptr;
    WCHAR                   szStreamName[MAX_PATH + 1];
    NTSTATUS                ntStatus;

    // Increase buffer size until all stream names fit into
    do
    {
        if ( ( pFsi = ( FILE_STREAM_INFORMATION * ) new BYTE[dwBufferSize] ) == nullptr )
            break;

        memset( pFsi, 0, dwBufferSize );

        ntStatus = NtQueryInformationFile(
                       hFile,
                       &IoStatus,
                       pFsi,
                       dwBufferSize,
                       FileStreamInformation
                   );

        if ( ntStatus != STATUS_BUFFER_OVERFLOW )
            break;

        delete [] pFsi;
        pFsi = nullptr;
        dwBufferSize += STREAMBUFFERSIZE;

    }
    while ( ntStatus == STATUS_BUFFER_OVERFLOW );

    if ( ntStatus == STATUS_SUCCESS )
    {
        // Loop over stream names and print them
        m_pFsi = pFsi;

        while ( true )
        {
            // Directories without special alternate streams do not return valid stream info
            if ( m_pFsi->StreamNameLength )
            {
                // Remember: StreamNameLength specifies the stream names length in BYTES!
                wcsncpy_s( szStreamName, m_pFsi->StreamName, m_pFsi->StreamNameLength / sizeof( WCHAR ) );
                // wcsncpy does not null terminate
                szStreamName[m_pFsi->StreamNameLength / sizeof( WCHAR )] = 0;

                //printf("\t%ls\n", szStreamName);
            }

            if ( m_pFsi->NextEntryOffset )
                m_pFsi = ( FILE_STREAM_INFORMATION * ) ( ( BYTE * )m_pFsi + m_pFsi->NextEntryOffset );
            else
                break;
        }
    }

    if ( pFsi )
        delete [] pFsi;

    return 0;
}


///sparse
//////////////////////////////////////////////////////////////////////////


#define ALLOWED_ATTRIBUTES (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)



BOOL QSparseFile::VolumeSupportsSparseFiles( LPCTSTR lpRootPathName )
{
    DWORD dwVolFlags;
    GetVolumeInformation( lpRootPathName, nullptr, MAX_PATH, nullptr, nullptr,
                          &dwVolFlags, nullptr, MAX_PATH );

    return ( dwVolFlags & FILE_SUPPORTS_SPARSE_FILES ) ? TRUE : FALSE;
}


HANDLE QSparseFile::CreateSparseFile( LPCTSTR lpFileName )
{
    // Create a normal file
    HANDLE hSparseFile = CreateFile( lpFileName, GENERIC_WRITE, 0, nullptr,
                                     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr );

    if ( hSparseFile == INVALID_HANDLE_VALUE )
        return hSparseFile;

    // Use the DeviceIoControl function with the FSCTL_SET_SPARSE control
    // code to mark the file as sparse. If you don't mark the file as sparse,
    // the FSCTL_SET_ZERO_DATA control code will actually write zero bytes to
    // the file instead of marking the region as sparse zero area.
    DWORD dwTemp = 0;
    DeviceIoControl( hSparseFile, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &dwTemp, nullptr );

    return hSparseFile;
}


void QSparseFile::SetSparseRange( HANDLE hSparseFile, LONGLONG start, LONGLONG size )
{
    // Specify the starting and the ending address (not the size) of the
    // sparse zero block
    FILE_ZERO_DATA_INFORMATION fzdi;
    fzdi.FileOffset.QuadPart = start;
    fzdi.BeyondFinalZero.QuadPart = start + size;

    // Mark the range as sparse zero block
    DWORD dwTemp;
    DeviceIoControl( hSparseFile, FSCTL_SET_ZERO_DATA, &fzdi, sizeof( fzdi ),
                     nullptr, 0, &dwTemp, nullptr );
}

BOOL QSparseFile::GetSparseFileSize( LPCTSTR lpFileName,  LARGE_INTEGER & liSparseFileSize, LARGE_INTEGER & liSparseFileCompressedSize )
{
    // Retrieves the size of the specified file, in bytes. The size includes
    // both allocated ranges and sparse ranges.
    HANDLE hFile = CreateFile( lpFileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr );


    if ( hFile == INVALID_HANDLE_VALUE )
        return FALSE;


    //LARGE_INTEGER liSparseFileSize;
    BOOL bo = GetFileSizeEx( hFile, &liSparseFileSize );

    // Retrieves the file's actual size on disk, in bytes. The size does not
    // include the sparse ranges.
    //LARGE_INTEGER liSparseFileCompressedSize;
    //
    liSparseFileCompressedSize.LowPart = GetCompressedFileSize( lpFileName, ( LPDWORD )&liSparseFileCompressedSize.HighPart );

    // Print the result
    // wprintf( L"\nFile total size: %I64uKB\nActual size on disk: %I64uKB\n",
    //	liSparseFileSize.QuadPart / 1024,
    //	liSparseFileCompressedSize.QuadPart / 1024 );

    CloseHandle( hFile );

    return bo;
}


//normal or sparse secure delete
void  QSparseFile::OverwriteRange( HANDLE hFile, LARGE_INTEGER iPos, LARGE_INTEGER iSize )
{
    const DWORD sz = 32 * 1024;
    BYTE buf[sz];
    memset( buf, 0xFF, sz ); //se suprascrie cu 0xFF

    DWORD dr = ::SetFilePointerEx( hFile, iPos , nullptr, FILE_BEGIN );

    if ( dr == INVALID_SET_FILE_POINTER )
    {
        if ( GetLastError() != NO_ERROR )
            throw QError();
    }


    LONGLONG count = 0, sizeRange = iSize.QuadPart;

    ///de rezolvat
    ///m_pDialog->SetFileProgressPos( 0 );


    DWORD  dwBytesWritten = 0;

    while ( iSize.QuadPart > 0 )
    {
        DWORD size  = min( sz, iSize.QuadPart );

        if ( !::WriteFile( hFile, buf, size, &dwBytesWritten, nullptr ) )
            throw QError();

        //if (dwBytesWritten < size)
        //	throw (DWORD)ERROR_HANDLE_DISK_FULL;

        iSize.QuadPart -= size;


        count += size;
        ///m_pDialog->SetFileProgressPos( count * ( 100.0 / sizeRange ) );


    }
}


void QSparseFile::OverwriteSparseFile( HANDLE file,  LARGE_INTEGER iFileSize )
{


    // Main loop querying and copying allocated area ranges
    FILE_ALLOCATED_RANGE_BUFFER queryrange;
    FILE_ALLOCATED_RANGE_BUFFER ranges[1024];
    DWORD nbytes = 0;
    BOOL bo;


    queryrange.FileOffset.QuadPart = 0;
    queryrange.Length = iFileSize;

    do
    {
        // This loop executes more than once only if the file has more than 1024 allocated areas
        bo = ::DeviceIoControl( file, FSCTL_QUERY_ALLOCATED_RANGES, &queryrange, sizeof( queryrange ), ranges, sizeof( ranges ), &nbytes, nullptr );

        if ( !bo )
        {
            if ( GetLastError() != ERROR_MORE_DATA )
                break;
        }

        int n = nbytes / sizeof( FILE_ALLOCATED_RANGE_BUFFER );		// Calculate the number of records returned

        for ( int i = 0; i < n; i++ )		// Main loop
        {
            OverwriteRange( file,  ranges[i].FileOffset, ranges[i].Length );


            //count += ranges[i].Length.QuadPart;
            //m_pDialog->SetFileProgressPos( count * (100.0 / FSize.QuadPart) );

        }

        // Set starting address and size for the next query
        if ( !bo && n > 0 )
        {
            queryrange.FileOffset.QuadPart = ranges[n - 1].FileOffset.QuadPart + ranges[n - 1].Length.QuadPart;
            queryrange.Length.QuadPart = iFileSize.QuadPart - queryrange.FileOffset.QuadPart;
        }
    }
    while ( !bo );	// Continue loop if ERROR_MORE_DATA



}

//sparse copy fragment
void   QSparseFile::CopySparseRange( HANDLE hFrom, HANDLE hTo, LARGE_INTEGER iPos, LARGE_INTEGER iSize )
{
    BYTE buf[32 * 1024];
    DWORD dwBytesRead, dwBytesWritten;

    LONG iPosHigh = iPos.HighPart;
    DWORD dr = ::SetFilePointer( hFrom, iPos.LowPart, &iPosHigh, FILE_BEGIN );

    if ( dr == INVALID_SET_FILE_POINTER )
    {
        if ( GetLastError() != NO_ERROR )
            throw QError();
    }

    iPosHigh = iPos.HighPart;
    dr = ::SetFilePointer( hTo, iPos.LowPart, &iPosHigh, FILE_BEGIN );

    if ( dr == INVALID_SET_FILE_POINTER )
    {
        if ( GetLastError() != NO_ERROR )
            throw QError();
    }

    while ( iSize.QuadPart > 0 )
    {
        DWORD sz = ( DWORD ) min( sizeof( buf ) , iSize.QuadPart );

        if ( !::ReadFile( hFrom, buf, sz , &dwBytesRead, nullptr ) )
            throw QError();

        if ( !::WriteFile( hTo, buf, dwBytesRead, &dwBytesWritten, nullptr ) )
            throw QError();

        if ( dwBytesWritten < dwBytesRead )
            throw QError();//(DWORD)ERROR_HANDLE_DISK_FULL;

        iSize.QuadPart -= dwBytesRead;
    }
}


bool  QSparseFile::CopySparse( QString src, QString dest )
{
    HANDLE hInFile, hOutFile;

    try
    {
        BY_HANDLE_FILE_INFORMATION bhfi;
        TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFName[_MAX_FNAME], szExt[_MAX_EXT];
        TCHAR szVolName[MAX_PATH];


        DWORD dwSN, dwMaxLen, dwVolFlags;
        BOOL bSparseCopy = TRUE;
        DWORD dr;

        _tsplitpath_s( src, szDrive, szDir, szFName, szExt );

        if ( _tcslen( szDrive ) )
        {
            _tcscat_s( szDrive, _T( "\\" ) );

            if ( !::GetVolumeInformation( szDrive, 0, 0, &dwSN, &dwMaxLen, &dwVolFlags, nullptr, 0 ) )
                throw QError();

            bSparseCopy = dwVolFlags & FILE_SUPPORTS_SPARSE_FILES;
        }


        // Target drive supports sparse files?
        if ( bSparseCopy )
        {
            // No need to check if source drive does not support
            _tsplitpath_s( dest, szDrive, szDir, szFName, szExt );

            if ( _tcslen( szDrive ) )
            {
                _tcscat_s( szDrive, _T( "\\" ) );

                if ( !::GetVolumeInformation( szDrive, szVolName, MAX_PATH, &dwSN, &dwMaxLen, &dwVolFlags, nullptr, 0 ) )
                    throw QError();

                bSparseCopy = dwVolFlags & FILE_SUPPORTS_SPARSE_FILES;
            }
        }

        hInFile = ::CreateFile( src, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr );

        if ( hInFile == INVALID_HANDLE_VALUE )
            throw QError();

        if ( !::GetFileInformationByHandle( hInFile, &bhfi ) )
            throw QError();

        bSparseCopy = bSparseCopy && ( bhfi.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE );	// Check if the source file is sparse

        hOutFile = ::CreateFile( dest, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr );

        if ( hOutFile == INVALID_HANDLE_VALUE )
            throw QError();

        if ( bSparseCopy )
        {
            // Perform sparse-aware copy

            // Get file size
            LARGE_INTEGER iFileSize;

            BOOL bo = GetFileSizeEx( hInFile, &iFileSize );


            DWORD dwTemp;

            if ( ! ::DeviceIoControl( hOutFile, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &dwTemp, nullptr ) )
                throw QError();

            // Main loop querying and copying allocated area ranges
            FILE_ALLOCATED_RANGE_BUFFER queryrange;
            FILE_ALLOCATED_RANGE_BUFFER ranges[1024];
            DWORD nbytes;


            queryrange.FileOffset.QuadPart = 0;
            queryrange.Length = iFileSize;

            do
            {
                // This loop executes more than once only if the file has more than 1024 allocated areas
                bo = ::DeviceIoControl( hInFile, FSCTL_QUERY_ALLOCATED_RANGES, &queryrange, sizeof( queryrange ), ranges, sizeof( ranges ), &nbytes, nullptr );

                if ( !bo )
                {
                    dr = ::GetLastError();

                    if ( dr != ERROR_MORE_DATA )
                        throw QError();
                }

                int n = nbytes / sizeof( FILE_ALLOCATED_RANGE_BUFFER );		// Calculate the number of records returned

                for ( int i = 0; i < n; i++ )		// Main loop
                    CopySparseRange( hInFile, hOutFile, ranges[i].FileOffset, ranges[i].Length );

                // Set starting address and size for the next query
                if ( !bo && n > 0 )
                {
                    queryrange.FileOffset.QuadPart = ranges[n - 1].FileOffset.QuadPart + ranges[n - 1].Length.QuadPart;
                    queryrange.Length.QuadPart = iFileSize.QuadPart - queryrange.FileOffset.QuadPart;
                }
            }
            while ( !bo );	// Continue loop if ERROR_MORE_DATA

            // Set end of file (required because there can be a sparse area in the end)
            dr = ::SetFilePointer( hOutFile, iFileSize.LowPart, &iFileSize.HighPart, FILE_BEGIN );

            if ( dr == INVALID_SET_FILE_POINTER )
            {
                dr = ::GetLastError();

                if ( dr != NO_ERROR )
                    throw QError();
            }

            if ( ! ::SetEndOfFile( hOutFile ) )
                throw QError();
        }

        else
        {
            // Not a sparse file, or sparse copy is impossible
            BYTE buf[32 * 1024];
            DWORD dwBytesRead, dwBytesWritten;

            do
            {
                if ( !::ReadFile( hInFile, buf, sizeof( buf ), &dwBytesRead, nullptr ) )
                    throw QError();

                if ( dwBytesRead )
                {
                    if ( !::WriteFile( hOutFile, buf, dwBytesRead, &dwBytesWritten, nullptr ) )
                        throw QError();

                    if ( dwBytesWritten < dwBytesRead )
                        throw ( DWORD )ERROR_HANDLE_DISK_FULL;
                }
            }
            while ( dwBytesRead == sizeof( buf ) );
        }

        ::CloseHandle( hInFile );

        // Set output file attributes
        if ( !::SetFileTime( hOutFile, &bhfi.ftCreationTime, &bhfi.ftLastAccessTime, &bhfi.ftLastWriteTime ) )
            throw QError();

        ::CloseHandle( hOutFile );

        if ( !::SetFileAttributes( dest,  bhfi.dwFileAttributes & ALLOWED_ATTRIBUTES ) )
            throw QError();
    }
    catch ( QError & e )
    {
        e.PrintMessage();
        return false;
    }

    return true;
}

